﻿using Autofac;
using IoTAdapter.BaseCore.Enums;
using IoTSocket.Core;
using IoTSocket.Sockets;
using LinqKit;
using Microsoft.Extensions.Logging;
using Redbus.Events;
using Redbus.Interfaces;
using SqlSugar;
using Warehouse.Services.Repository;
using WCS.Entities;
using WCS.IServices;

namespace WCS.Services.ManagerService
{
    /// <summary>
    /// 设备管理方法
    /// </summary>
    public class DeviceManagerService : IDeviceManager
    {
        #region Fileds

        //日志记录
        private readonly ILogger<DeviceManagerService> logger;

        //生命周期
        private readonly ILifetimeScope lifetimeScope;

        // 事件
        private readonly IEventBus eventBus;

        // 计时器管理
        private readonly System.Timers.Timer connectTimerManager = new(5000);

        // 任务
        private readonly ICreateJob createJob;

        // 设备 并发集合
        private readonly ConcurrentList<Device> disConnDevices = new();

        #endregion

        #region Property

        /// <summary>
        /// 设备集合
        /// </summary>
        public List<Device> Devices { get; set; }

        /// <summary>
        /// 对接口
        /// </summary>
        public ConcurrentList<TransferPort> Ports { get; set; }

        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        public DeviceManagerService(ILogger<DeviceManagerService> logger, ILifetimeScope lifetimeScope, IEventBus eventBus, ICreateJob createJob)
        {
            this.logger = logger;
            this.lifetimeScope = lifetimeScope;
            this.eventBus = eventBus;
            this.createJob = createJob;
            Initial();
        }


        #region 断线重连处理

        /// <summary>
        /// 重连事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void ConnectTimerManager_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            connectTimerManager.Stop();
            if (disConnDevices.Where(x => x.Enable).Count() > 0)
            {
                disConnDevices.Where(x => x.Enable).AsParallel().ForEach(x =>
                {
                    x.Close(true);
                    x.Operation.Connected -= Operation_Connected;
                    x.Operation.Connected += Operation_Connected;
                    x.Operation.Disconnected -= Operation_Disconnected;
                    x.Operation.Disconnected += Operation_Disconnected;
                    logger.LogInformation($"The device ({x.IP}) has begin to connect....");
                    x.Connect();
                });
            }
            //Thread.Sleep( 3000 );
            disConnDevices.RemoveAll(x => x.IsConnected);
            logger.LogInformation($"The disconencted devices count is:{0}", disConnDevices.Count);
            connectTimerManager.Start();
        }

        /// <summary>
        /// 断线事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Operation_Disconnected(object sender, string e)
        {
            var device = sender as TcpClientBase;
            logger.LogInformation("The device {0}  has disconencted...", device.IP);
        }


        /// <summary>
        /// 连接成功事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Operation_Connected(object sender, string e)
        {
            var device = sender as TcpClientBase;
            logger.LogInformation("The device {0}  has connected success...", device.IP);
        }

        #endregion

        //#region 订阅事件

        ///// <summary>
        ///// 出库事件
        ///// </summary>
        ///// <param name="outStorage"></param>
        //private async void OnOutStorageEvent(PayloadEvent<Tuple<int, string>> outStorage)
        //{
        //    (int key, string materialNumber) = outStorage.Payload;

        //    #region 暂时不用

        //    //var port = Ports.FirstOrDefault();
        //    //var storageList = new List<Storage>();
        //    //var storages = await storageRepository.QueryAsync();
        //    //var storageCount = storages.Where(i => i.IsFull).OrderBy(i => i.CreateTime).GroupBy(i => i.Content, i => i.MaterialId).Where(i => i.Count() > 1).ToList();

        //    //if (storageList.Count == 24)
        //    //{
        //    //    #region 之前的写法

        //    //    // 发送出库
        //    //    //var materials = await materialRepository.QueryAsync();
        //    //    //var materialList = new List<Material>();
        //    //    //var numberSameCount = materials.Where(i => i.IsOutStorage == false).OrderBy(i => i.CreateDateTime).GroupBy(i => i.MaterialNumber).Where(i => i.Count() > 1).ToList();
        //    //    //if (numberSameCount.Count > 24)
        //    //    //{
        //    //    //    foreach (var material in numberSameCount)
        //    //    //    {
        //    //    //        materialList = materials.Where(i => i.MaterialNumber == material.Key).ToList();
        //    //    //    }
        //    //    //}
        //    //    //if (materialList.Count > 0)
        //    //    //{
        //    //    //    foreach (var item in materialList)
        //    //    //    {
        //    //    //        var storageInfo = await storageRepository.FindAsync(item.StorageId);
        //    //    //        if (storageInfo != null)
        //    //    //        {
        //    //    //            storageInfo.CurrentNumber--;
        //    //    //            item.IsOutStorage = true;
        //    //    //            await materialRepository.UpdateAsync(item);
        //    //    //            await storageRepository.Context.CopyNew().Updateable(storageInfo).UpdateColumns(i => new { i.CurrentNumber }).ExecuteCommandAsync();
        //    //    //        }
        //    //    //        if (!string.IsNullOrEmpty(item.MaterialNumber))
        //    //    //        {
        //    //    //            var jobNumber = DateTime.Now.ToString("yyMMddHHmmssfff");
        //    //    //            var result = stackerHelper.OutStroage(Convert.ToInt32(jobNumber), storageInfo.Arrange, storageInfo.CurrentColumns, storageInfo.CurrentStoreyNumber, storageInfo.Arrange, storageInfo.CurrentColumns, storageInfo.CurrentStoreyNumber);
        //    //    //            if (result)
        //    //    //            {
        //    //    //                logger.LogInformation($"出库成功：{result}");
        //    //    //            }
        //    //    //        }
        //    //    //    }
        //    //    //}

        //    //    #endregion

        //    //    foreach (var material in storageCount)
        //    //    {
        //    //        storageList = storages.Where(i => i.Content == material.Key).ToList();
        //    //    }
        //    //    if (storageList.Count > 0)
        //    //    {
        //    //        foreach (var item in storageList)
        //    //        {
        //    //            var storageInfo = await storageRepository.FindAsync(item.Id);
        //    //            if (storageInfo != null)
        //    //            {
        //    //                storageInfo.CurrentNumber--;
        //    //                await storageRepository.Context.CopyNew().Updateable(storageInfo).UpdateColumns(i => new { i.CurrentNumber }).ExecuteCommandAsync();
        //    //            }
        //    //            if (!string.IsNullOrEmpty(item.Content))
        //    //            {
        //    //                var jobNumber = DateTime.Now.ToString("yyMMddHHmmssfff");
        //    //                var result = stackerHelper.OutStroage(Convert.ToInt32(jobNumber), storageInfo.Arrange, storageInfo.CurrentColumns, storageInfo.CurrentStoreyNumber, storageInfo.Arrange, storageInfo.CurrentColumns, storageInfo.CurrentStoreyNumber);
        //    //                if (result)
        //    //                {
        //    //                    logger.LogInformation($"出库成功：{result}");
        //    //                }
        //    //            }
        //    //        }
        //    //    }
        //    //}
        //    //else if (storageList.Count == 26)
        //    //{
        //    //    foreach (var material in storageCount)
        //    //    {
        //    //        storageList = storages.Where(i => i.Content == material.Key).ToList();
        //    //    }
        //    //    if (storageList.Count > 0)
        //    //    {
        //    //        foreach (var item in storageList)
        //    //        {
        //    //            var storageInfo = await storageRepository.FindAsync(item.Id);
        //    //            if (storageInfo != null)
        //    //            {
        //    //                storageInfo.CurrentNumber--;
        //    //                await storageRepository.Context.CopyNew().Updateable(storageInfo).UpdateColumns(i => new { i.CurrentNumber }).ExecuteCommandAsync();
        //    //            }
        //    //            if (!string.IsNullOrEmpty(item.Content))
        //    //            {
        //    //                var jobNumber = DateTime.Now.ToString("yyMMddHHmmssfff");
        //    //                var result = stackerHelper.OutStroage(Convert.ToInt32(jobNumber), storageInfo.Arrange, storageInfo.CurrentColumns, storageInfo.CurrentStoreyNumber, storageInfo.Arrange, storageInfo.CurrentColumns, storageInfo.CurrentStoreyNumber);
        //    //                if (result)
        //    //                {
        //    //                    logger.LogInformation($"出库成功：{result}");
        //    //                }
        //    //            }
        //    //        }
        //    //    }
        //    //}
        //    //else if (storageList.Count == 30)
        //    //{
        //    //    foreach (var material in storageCount)
        //    //    {
        //    //        storageList = storages.Where(i => i.Content == material.Key).ToList();
        //    //    }
        //    //    if (storageList.Count > 0 && storageList.Count == 30)
        //    //    {
        //    //        foreach (var item in storageList)
        //    //        {
        //    //            var storageInfo = await storageRepository.FindAsync(item.Id); 
        //    //            if (storageInfo != null)
        //    //            {
        //    //                storageInfo.CurrentNumber--;
        //    //                await storageRepository.Context.CopyNew().Updateable(storageInfo).UpdateColumns(i => new { i.CurrentNumber }).ExecuteCommandAsync();
        //    //            }
        //    //            if (!string.IsNullOrEmpty(item.Content))
        //    //            {
        //    //                var jobNumber = DateTime.Now.ToString("yyMMddHHmmssfff");
        //    //                var result = stackerHelper.OutStroage(Convert.ToInt32(jobNumber), storageInfo.Arrange, storageInfo.CurrentColumns, storageInfo.CurrentStoreyNumber, storageInfo.Arrange, storageInfo.CurrentColumns, storageInfo.CurrentStoreyNumber);
        //    //                if (result)
        //    //                {
        //    //                    logger.LogInformation($"出库成功：{result}");
        //    //                }
        //    //            }
        //    //        }
        //    //    }
        //    //}

        //    #endregion

        //    var isOk = string.Empty;
        //    // 需要物料类型 -- 每拖固定多少数量
        //    var materials = await materialRepository.QueryAsync();
        //    var materialList = new List<Material>();
        //    var numberSameCount = materials.Where(i => i.IsOutStorage == false).OrderBy(i => i.CreateDateTime).GroupBy(i => i.MaterialNumber, i => i.Id).Where(i => i.Count() > 1).ToList();
        //    if (numberSameCount.Count > 0)
        //    {
        //        foreach (var material in numberSameCount)
        //        {
        //            materialList = materials.Where(i => i.MaterialNumber == material.Key).ToList();
        //        }
        //    }
        //    if (materialList.Count > 0 && materialList.Count == 30)
        //    {
        //        foreach (var item in materialList)
        //        {
        //            var storageInfo = await storageRepository.FindAsync(item.StorageId);
        //            if (storageInfo != null)
        //            {
        //                storageInfo.CurrentNumber--;
        //                item.IsOutStorage = true;
        //                await materialRepository.UpdateAsync(item);
        //                await storageRepository.Context.CopyNew().Updateable(storageInfo).UpdateColumns(i => new { i.CurrentNumber }).ExecuteCommandAsync();
        //                isOk = item.MaterialNumber;
        //                var jobNumber = DateTime.Now.ToString("yyMMddHHmmssfff");
        //                var result = stackerHelper.OutStroage(Convert.ToInt32(jobNumber), storageInfo.Arrange, storageInfo.CurrentColumns, storageInfo.CurrentStoreyNumber, storageInfo.Arrange, storageInfo.CurrentColumns, storageInfo.CurrentStoreyNumber);
        //                logger.LogInformation($"该{isOk}物料号，出库成功:{result}");
        //            }
        //        }
        //    }
        //}

        ///// <summary>
        ///// 入库事件
        ///// 备注：入库方式，
        /////         1、是根据监控设备方式来产生任务？ 【未得知自动化设备】
        /////         2、还是通过调接口的方式在产生任务 【已创建相应的接口并已调试】
        ///// </summary>
        ///// <param name="outStorage"></param>
        //private async void OnPutStorageEvent(PayloadEvent<Tuple<int, string>> outStorage)
        //{
        //    (int key, string materialNumber) = outStorage.Payload;

        //    var storages = await storageRepository.QueryAsync();
        //    var storageInfo = new Storage();
        //    if (storages.Count > 0)
        //    {
        //        storageInfo = storages.Where(i => !i.IsFull).OrderByDescending(i => i.CurrentNumber).FirstOrDefault();
        //        if (storageInfo != null)
        //        {
        //            if (storageInfo.CountColumns != storageInfo.CurrentColumns)
        //            {
        //                if (storageInfo.CurrentColumnsNumber == storageInfo.CurrentColumnsCount)
        //                {
        //                    storageInfo.CurrentColumns++;
        //                    storageInfo.CurrentColumnsNumber = 0;
        //                    storageInfo.CurrentStoreyNumber = 0;
        //                    await storageRepository.Context.CopyNew().Updateable(storageInfo).UpdateColumns(i => new { i.CurrentColumns, i.CurrentColumnsNumber, i.CurrentStoreyNumber }).ExecuteCommandAsync();
        //                }
        //                storageInfo.CurrentColumnsNumber++;
        //                storageInfo.CurrentStoreyNumber++;
        //                storageInfo.CurrentNumber++;
        //                await storageRepository.Context.CopyNew().Updateable(storageInfo).UpdateColumns(i => new { i.CurrentColumnsNumber, i.CurrentStoreyNumber, i.CurrentNumber }).ExecuteCommandAsync();

        //                #region 最初写法

        //                //// 任务 ID
        //                //port.BelongDevice.Operation.Write("0", job.Id);

        //                //// 下发的源排
        //                //port.BelongDevice.Operation.Write("4", "");

        //                //// 下发的源列
        //                //port.BelongDevice.Operation.Write("6", "");

        //                //// 下发的源层
        //                //port.BelongDevice.Operation.Write("8", "");

        //                //// 下发的目标排
        //                //port.BelongDevice.Operation.Write("10", "");

        //                //// 下发的目标列
        //                //port.BelongDevice.Operation.Write("12", "");

        //                //// 下发的目标层
        //                //port.BelongDevice.Operation.Write("14", "");

        //                //// 任务类型 1：取放（去源位置取货，目标位置放货）；2：取货（去源位置取货）；3：放货（去目标位置放货）；4：行走（移动到源位置）；5：预留；6：预留；7：任务删除；
        //                //port.BelongDevice.Operation.Write("16", (ushort)2);

        //                //// 预留
        //                //port.BelongDevice.Operation.Write("18", "");

        //                //// 校验码 = 下发的源排 + 下发的源列 + 下发的源层 + 下发的目标排 + 下发的目标列 + 下发的目标层 + 任务类型
        //                //port.BelongDevice.Operation.Write("20", "");

        //                //// 1：任务下发完成，启动下发的任务写1; 0：堆垛机响应任务后，堆垛机PLC置为0
        //                //port.BelongDevice.Operation.Write("24", (ushort)1);

        //                #endregion

        //                var jobNumber = DateTime.Now.ToString("yyMMddHHmmssfff");
        //                var result = stackerHelper.PutStroage(Convert.ToInt32(jobNumber), storageInfo.Arrange, storageInfo.CurrentColumns, storageInfo.CurrentStoreyNumber, storageInfo.Arrange, storageInfo.CurrentColumns, storageInfo.CurrentStoreyNumber);
        //                if (result)
        //                {
        //                    logger.LogInformation($"入库成功：{result}");
        //                }
        //            }
        //        }
        //    }
        //}

        /// <summary>
        /// 实体改变信息
        /// </summary>
        /// <param name="changeEventArgs"></param>
        private void OnChangeEvent(PayloadEvent<Tuple<string, ModelChangeMessage>> changeEventArgs)
        {
            (string key, ModelChangeMessage message) = changeEventArgs.Payload;

            if (key == "Device")
            {
                Devices.ToList().ForEach(x => x.Close(true));

                using var scope = lifetimeScope.BeginLifetimeScope();
                {
                    connectTimerManager.Stop();
                    var dbContext = scope.Resolve<Repository<Device>>();

                    disConnDevices.Clear();
                    //设备初始化
                    Devices = dbContext.AsQueryable().Includes(x => x.TransferPorts).ToList();

                    Devices.Where(d => d.TransferPorts != null).ForEach(d => d.TransferPorts.ForEach(t => t.BelongDevice = d));
                    Ports = new ConcurrentList<TransferPort>(Devices.Where(d => d.TransferPorts != null).SelectMany(x => x.TransferPorts).ToList());
                    connectTimerManager.Start();
                }
            }

            if (key == "TransPort")
            {
                Devices.ToList().ForEach(x => x.Close(true));
                Ports.Clear();
                disConnDevices.Clear();
                using var scope = lifetimeScope.BeginLifetimeScope();
                {
                    connectTimerManager.Stop();
                    var dbContext = scope.Resolve<Repository<Device>>();
                    //设备初始化
                    Devices = dbContext.AsQueryable().Includes(x => x.TransferPorts).ToList();
                    disConnDevices.Clear();
                    Devices.Where(d => d.TransferPorts != null).ForEach(d => d.TransferPorts.ForEach(t => t.BelongDevice = d));
                    Ports = new ConcurrentList<TransferPort>(Devices.Where(d => d.TransferPorts != null).SelectMany(x => x.TransferPorts).ToList());
                    connectTimerManager.Start();
                }
            }
        }

        //#endregion

        #region Method

        /// <summary>
        /// 监控设备主方法
        /// </summary>
        public async void DeviceMonitor()
        {
            using (var scope = lifetimeScope.BeginLifetimeScope())
            {
                #region 【1】处理设备连接监控数值

                Devices?.Where(x => x.Type != EthernetDeviceVersion.HttpClient && x.Enable).AsParallel().ForEach(d =>
                {
                    try
                    {
                        if (d.IsConnected)
                        {
                            scope.ResolveNamed<IDeviceMonitor>(d.MonitorType).MonitorDevice(d);
                        }
                        else
                        {
                            if (!disConnDevices.Any(x => x.IP == d.IP))
                            {
                                logger.LogInformation($"The device disconencted, now ({d.IP}) Add to disconect Devices");
                                disConnDevices.Add(d);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        logger.LogError($"monitor device-({d.Name}) occur the error, error msg:{ex.Message}");
                    }
                });

                #endregion

            }

            #region【2】处理状态

            //任务产生
            if (Ports.Any(x => x.Status == PortStatus.Calling))
            {
                var callingPorts = Ports.Where(x => x.Status == PortStatus.Calling).ToList();
                foreach (var port in callingPorts)
                {
                    await createJob.CreateJob(port, Ports.ToList());
                }
            }

            #endregion
        }

        /// <summary>
        /// 初始化
        /// </summary>
        public void Initial()
        {
            using var scope = lifetimeScope.BeginLifetimeScope();
            {

                //   var Db = scope.Resolve<ISqlSugarClient>();
                //var dbContext = Db.CopyNew();
                var dbContext = scope.Resolve<Repository<Device>>();

                //设备初始化
                Devices = dbContext.AsQueryable().Includes(x => x.TransferPorts).ToList();
                // Devices = await dbContext.Queryable<Device>().Includes(x => x.TransferPorts).ToListAsync();
                Devices.Where(d => d.TransferPorts != null).ForEach(d => d.TransferPorts.ForEach(t => t.BelongDevice = d));
                Ports = new ConcurrentList<TransferPort>(Devices.Where(d => d.TransferPorts != null).SelectMany(x => x.TransferPorts).ToList());
            }
            connectTimerManager.Elapsed += ConnectTimerManager_Elapsed;
            connectTimerManager.Start();
            //事件订阅

            // 监听更改料口和设备
            eventBus.Subscribe<PayloadEvent<Tuple<string, ModelChangeMessage>>>(OnChangeEvent);

            //// 出库
            //eventBus.Subscribe<PayloadEvent<Tuple<int, string>>>(OnOutStorageEvent);

            //// 入库
            //eventBus.Subscribe<PayloadEvent<Tuple<int, string>>>(OnPutStorageEvent);
        }
        #endregion
    }
}
